package com.gopay.bis.common.util;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.regex.Pattern;

import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.FileSystemResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;

public class TransUtils {
    /** 金额表达式. */
    private static final String AMOUNT_REGULAR_EX = "^(\\d{1,10}(\\.\\d{0,2})?)$";
    /** 金额模式. */
    private static final Pattern PATTERN = Pattern.compile(AMOUNT_REGULAR_EX);
    /** 100, 工行金额放大一百倍. */
    private static final BigDecimal ONEHUNDRED = new BigDecimal(100);

    public static final Integer LOCK = new Integer(0);
    /** 时间格式yyMMddHHmmSSS. */
    private static final String TIME_FORMAT_TWO_BIT_YEAR_SECOND = "yyMMddHHmmss";
    /** Suffix. */
    private static int suffix = 0;
    /** 长度. */
    private static final int LENGTH = 1000;
    /** 两位. */
    private static final int TWO_BIT = 10;
    /** 三位. */
    private static final int THREE_BIT = 100;
    /** 插一个零. */
    private static final char SUFFIX_ONE_ZERO = '0';
    /** 插两个零. */
    private static final String SUFFIX_TWO_ZERO = "00";
    
    
    /**
     * 对一金额字符串进行去小数点操作, 等于对输入金额放大一百倍, 返回金额字符串不能包括小数点.
     * 如amountConvert2Fen("100.5")则返回"10050".
     * 
     * @param amountStr
     *            必须遵循正则表达式<code>^(\\d{1,10}(\\.\\d{0,2})?)$</code>, 否则抛出
     *            <code>IllegalArgumentException</code>异常.
     * @return 返回金额字符串为原金额的一百倍.
     */
    public static String amountConvert2Fen(String amountStr) {

        if (!PATTERN.matcher(amountStr).matches()) {
            throw new IllegalArgumentException(amountStr + "输入的金额格式有误，无法转化金额为分");
        }
        BigDecimal originalAmount = new BigDecimal(amountStr);
        String resultStr = originalAmount.multiply(ONEHUNDRED).setScale(0).toString();
        return resultStr;
    }
    
    /**
     * 对一金额字符串进行加小数点操作, 等于对输入金额缩小一百倍
     * 如amountConvert2Fen("1")则返回"0.01".
     * 
     * @param amountStr
     *            必须遵循正则表达式<code>^(\\d{1,10}(\\.\\d{0,2})?)$</code>, 否则抛出
     *            <code>IllegalArgumentException</code>异常.
     * @return 返回金额字符串为原金额的0.01
     */
    public static String amountConvert2Yuan(String amountStr) {

        if (!PATTERN.matcher(amountStr).matches()) {
            throw new IllegalArgumentException(amountStr + "输入的金额格式有误，无法转化金额为分");
        }
        BigDecimal originalAmount = new BigDecimal(amountStr);
        String resultStr = originalAmount.divide(ONEHUNDRED).toString();
        return resultStr;
    }
    

    /**
     * Fetch designated field value in the message body according to the
     * appointed tag pair. If there are more than one open tag or close tag,
     * then return the string value between first open tag and first close tag;
     * or throw <code>TransformException</code>.
     * 
     * @param beginTag
     *            起始标签.
     * @param endTag
     *            结束标签.
     * @param msg
     *            Message body as <code>String</code>.
     * @return String 域值.
     */
    public static String getMsgField(String beginTag, String endTag, String msg) {
        int beginIndex = msg.indexOf(beginTag);
        int endIndex = msg.indexOf(endTag);
        if (beginIndex == -1 || endIndex == -1) {
            throw new RuntimeException("Unintegrated message, with absence of " + beginTag);
        }
        String returnCode = msg.substring(beginIndex + beginTag.length(), endIndex);
        return returnCode;
    }

    /**
     * 生成yyMMddhhmmssSSS格式的时间字符串，工行报文PackageID字段用.
     * 
     * @return yyMMddhhmmssSSS格式的时间字符串.
     */
    public static String getPackageID() {
        StringBuffer stringBuffer = new StringBuffer(getTimeString(TIME_FORMAT_TWO_BIT_YEAR_SECOND));
        int tmp = 0;
        synchronized (LOCK) {
            suffix %= LENGTH;
            tmp = suffix;
            suffix++;
        }
        if (tmp < TWO_BIT) {
            stringBuffer.append(SUFFIX_TWO_ZERO).append(tmp);
        } else if (tmp < THREE_BIT) {
            stringBuffer.append(SUFFIX_ONE_ZERO).append(tmp);
        } else {
            stringBuffer.append(tmp);
        }
        return stringBuffer.toString();
    }

    /**
     * 取得时间字符串.
     * 
     * @param timeFormat
     *            指定时间字符串的格式, 参考{@link java.text.SimpleDateFormat}.
     * @return 以timeFormat格式返回时间.
     */
    private static String getTimeString(String timeFormat) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(timeFormat);
        String sendTime = simpleDateFormat.format(new Date());
        return sendTime;
    }
    
	/**
	 * Get appointed file content according to the <code>filePath</code>.
	 * This method will try read the file in following sequence:
	 * <ul>
	 * <li>1. From classpath.
	 * <li>2. From file system, if 1 failed.
	 * </ul>
	 * So, <code>filePath</code> can be one of following forms:
	 * <ul>
	 * <li>1. Prefixed with <code>classpath:</code> indicate a classpath
	 * resource.
	 * <li>2. Prefixed with <code>file:</code> indicate an absolute file path
	 * resource.
	 * <li>3. Plain paths will always be interpreted as relative to the current
	 * VM working directory, even if they start with a slash.
	 * </ul>
	 * 
	 * @param filePath
	 *            the path of the file.
	 * @return file content.
	 * @throws FileNotFoundException
	 *             if appointed file can not be read or not exist.
	 */
	public static byte[] getFile(String filePath) throws FileNotFoundException {
		try {
			ResourceLoader resourceLoader = new DefaultResourceLoader();
			Resource resource = resourceLoader.getResource(filePath);
			InputStream inputStream = null;
			if (resource.exists()) {
				inputStream = resource.getInputStream();
			} else {
				resourceLoader = new FileSystemResourceLoader();
				resource = resourceLoader.getResource(filePath);
				if (resource.exists()) {
					inputStream = resource.getInputStream();
				} else {
					inputStream = new FileInputStream(filePath);
				}
			}
			byte[] fileByte = new byte[inputStream.available()];
			int fileLength = inputStream.read(fileByte);
			inputStream.close();
			if (fileLength >= 0) {
				byte[] targetFile = new byte[fileLength];
				System.arraycopy(fileByte, 0, targetFile, 0, fileLength);
				return targetFile;
			} else {
				throw new FileNotFoundException("File: [" + filePath
						+ "] can not be read or is empty");
			}
		} catch (Exception e) {
			if (e instanceof FileNotFoundException) {
				throw (FileNotFoundException) e;
			}
			throw new FileNotFoundException("File: [" + filePath
					+ "] can not be read");
		}
	}
	
	/**
	 * 民生银行读取证书文件.
	 * 
	 * @param filename
	 *            文件名.
	 * @return 证书文件二进制流.
	 * @throws Exception
	 *             读取出错.
	 */
	public static byte[] readCertFileForCMBCB2C(String filename)
			throws Exception {
		try {
			FileInputStream fileInStream = new FileInputStream(filename);
			ByteArrayOutputStream fileByteStream = new ByteArrayOutputStream();
			int i = 0;
			while ((i = fileInStream.read()) != -1) {
				fileByteStream.write(i);
			}
			fileInStream.close();
			return fileByteStream.toByteArray();
		} catch (Exception e) {
			throw new Exception(e.getMessage());
		}

	}
	
	/**
	 * Get the canonical file path according to the relative or classpath file
	 * path.
	 * 
	 * @param filePath
	 *            relative or classpath file path.
	 * @return canonical file path, or return <code>filePath</code> if
	 *         designated file does not exist or can not be read due to
	 *         permission.
	 */
	public static String getCanonicalFilePath(String filePath) {
		try {
			ResourceLoader resourceLoader = new DefaultResourceLoader();
			Resource resource = resourceLoader.getResource(filePath);
			File file = null;
			if (resource.exists()) {
				file = resource.getFile();
			} else {
				resourceLoader = new FileSystemResourceLoader();
				resource = resourceLoader.getResource(filePath);
				if (resource.exists()) {
					file = resource.getFile();
				} else {
					return filePath;
				}
			}
			if (file != null && file.exists() && file.isFile()) {
				String absFilePath = file.getCanonicalPath();
				return absFilePath;
			} else {
				return filePath;
			}
		} catch (Exception e) {
			return filePath;
		}
	}
	
}
